#include "epoll_selector.h"

namespace tinySrv
{

  EPollSelector::EPollSelector(int listener, int maxConnectionCount)
    : EPoll(-1)
    , Events(maxConnectionCount + 1)
    , Listener(listener)
  {
    if ((EPoll = epoll_create1(0)) == -1)
      throw EPollSelectorException("Can't create epoll");
    epoll_event Event = { 0 };
    Event.events = EPOLLIN;
    Event.data.fd = listener;     
    if (epoll_ctl(EPoll, EPOLL_CTL_ADD, listener, &Event) == -1)
    {
      CloseEPoll();
      throw EPollSelectorException("Can't add listen socket to epoll");
    }
  }
  
  EPollSelector::~EPollSelector()
  {
    CloseEPoll();
  }
  
  void EPollSelector::AddNewSocket(int newSocket, void *data)
  {
    epoll_event Event = { 0 };
    Event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
    Event.data.fd = newSocket;
    Event.data.ptr = data;
    if (epoll_ctl(EPoll, EPOLL_CTL_ADD, newSocket, &Event) == -1)
      throw EPollSelectorException("Failed to add new socket to epoll");
  }
  
  void EPollSelector::Select(unsigned long waitTimeout, ISelectorCallback *callback)
  {
    int Count = epoll_wait(EPoll, &Events[0], Events.size(), waitTimeout);
    if (Count == -1)
      throw EPollSelectorException("Failed to epoll wait");
    for (int i = 0 ; i < Count ; ++i)
    {
      if (Events[i].data.fd == Listener)
      {
        callback->ProcessSocket(Listener, ISelectorCallback::stOnConnect, 0);
        continue;
      }
      if (Events[i].events & EPOLLRDHUP)
      {
        callback->ProcessSocket(Events[i].data.fd, ISelectorCallback::stOnClose, Events[i].data.ptr);
        continue;
      }
      if (!callback->ProcessSocket(Events[i].data.fd, ISelectorCallback::stOnRead, Events[i].data.ptr))
        break;
    }
  }

  void EPollSelector::CloseEPoll()
  {
    close(EPoll);
  }
  
}
